#include <stdio.h>
#include <string>
#include <iostream>
#include <signal.h>
#include "libwebsockets.h"

using namespace std;

// static int protocol0_callback( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len ) {
//     struct session_data *data = (struct session_data *) user;
//     switch ( reason ) {
//         case LWS_CALLBACK_ESTABLISHED:       // 当服务器和客户端完成握手后
//             break;
//         case LWS_CALLBACK_RECEIVE:           // 当接收到客户端发来的帧以后
//             // 判断是否最后一帧
//             data->fin = lws_is_final_fragment( wsi );
//             // 判断是否二进制消息
//             data->bin = lws_frame_is_binary( wsi );
//             // 对服务器的接收端进行流量控制，如果来不及处理，可以控制之
//             // 下面的调用禁止在此连接上接收数据
//             lws_rx_flow_control( wsi, 0 );

//             // 业务处理部分，为了实现Echo服务器，把客户端数据保存起来
//             memcpy( &data->buf[ LWS_PRE ], in, len );
//             data->len = len;

//             // 需要给客户端应答时，触发一次写回调
//             lws_callback_on_writable( wsi );
//             break;
//         case LWS_CALLBACK_SERVER_WRITEABLE:   // 当此连接可写时
//             lws_write( wsi, &data->buf[ LWS_PRE ], data->len, LWS_WRITE_TEXT );
//             // 下面的调用允许在此连接上接收数据
//             lws_rx_flow_control( wsi, 1 );
//             break;
//     }
//     // 回调函数最终要返回0，否则无法创建服务器
//     return 0;
// }

// int main() {
// 	cout<<"websocket server demo"<<endl;
// 	// 信号处理函数
//     signal( SIGTERM, sighdl );

//     struct lws_context_creation_info ctx_info = { 0 };
//     ctx_info.port = 9090;
//     ctx_info.iface = NULL; // 在所有网络接口上监听
//     ctx_info.protocols = protocols;
//     ctx_info.gid = -1;
//     ctx_info.uid = -1;
//     ctx_info.options = LWS_SERVER_OPTION_VALIDATE_UTF8;
//     struct lws_context *context = lws_create_context( &ctx_info );
//     while ( !exit_sig ) {
//         lws_service( context, 1000 );
//     }
//     lws_context_destroy( context );
// 	return 0;
// }



// #define LWS_PLUGIN_STATIC
// #include "protocol_lws_minimal.c"

// static struct lws_protocols protocols[] = {
// 	{ "http", lws_callback_http_dummy, 0, 0 },
// 	LWS_PLUGIN_PROTOCOL_MINIMAL,
// 	{ NULL, NULL, 0, 0 } /* terminator */
// };

// static const lws_retry_bo_t retry = {
// 	.secs_since_valid_ping = 3,
// 	.secs_since_valid_hangup = 10,
// };

// static int interrupted;

// static const struct lws_http_mount mount = {
// 	/* .mount_next */		NULL,		/* linked-list "next" */
// 	/* .mountpoint */		"/",		/* mountpoint URL */
// 	/* .origin */			"./mount-origin",  /* serve from dir */
// 	/* .def */			"index.html",	/* default filename */
// 	/* .protocol */			NULL,
// 	/* .cgienv */			NULL,
// 	/* .extra_mimetypes */		NULL,
// 	/* .interpret */		NULL,
// 	/* .cgi_timeout */		0,
// 	/* .cache_max_age */		0,
// 	/* .auth_mask */		0,
// 	/* .cache_reusable */		0,
// 	/* .cache_revalidate */		0,
// 	/* .cache_intermediaries */	0,
// 	/* .origin_protocol */		LWSMPRO_FILE,	/* files in a dir */
// 	/* .mountpoint_len */		1,		/* char count */
// 	/* .basic_auth_login_file */	NULL,
// };

// void sigint_handler(int sig)
// {
// 	interrupted = 1;
// }

// int main(int argc, const char **argv)
// {
// 	struct lws_context_creation_info info;
// 	struct lws_context *context;
// 	const char *p;
// 	int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
// 			 for LLL_ verbosity above NOTICE to be built into lws,
// 			 * lws must have been configured and built with
// 			 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE 
// 			/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
// 			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
// 			/* | LLL_DEBUG */;

// 	signal(SIGINT, sigint_handler);

// 	if ((p = lws_cmdline_option(argc, argv, "-d")))
// 		logs = atoi(p);

// 	lws_set_log_level(logs, NULL);
// 	lwsl_user("LWS minimal ws server | visit http://localhost:7681 (-s = use TLS / https)\n");

// 	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
// 	info.port = 7681;
// 	info.mounts = &mount;
// 	info.protocols = protocols;
// 	info.vhost_name = "localhost";
// 	info.options =
// 		LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;

// #if defined(LWS_WITH_TLS)
// 	if (lws_cmdline_option(argc, argv, "-s")) {
// 		lwsl_user("Server using TLS\n");
// 		info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
// 		info.ssl_cert_filepath = "localhost-100y.cert";
// 		info.ssl_private_key_filepath = "localhost-100y.key";
// 	}
// #endif

// 	if (lws_cmdline_option(argc, argv, "-h"))
// 		info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK;

// 	if (lws_cmdline_option(argc, argv, "-v"))
// 		info.retry_and_idle_policy = &retry;

// 	context = lws_create_context(&info);
// 	if (!context) {
// 		lwsl_err("lws init failed\n");
// 		return 1;
// 	}

// 	while (n >= 0 && !interrupted)
// 		n = lws_service(context, 0);

// 	lws_context_destroy(context);

// 	return 0;
// }

static volatile int exit_sig = 0;
#define MAX_PAYLOAD_SIZE  10 * 1024
 
void sighdl( int sig ) {
    lwsl_notice( "%d traped", sig );
    exit_sig = 1;
}
 
/**
 * 会话上下文对象，结构根据需要自定义
 */
struct session_data {
    int msg_count;
    unsigned char buf[LWS_PRE + MAX_PAYLOAD_SIZE];
    int len;
    bool bin;
    bool fin;
};
 
static int protocol_my_callback( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len ) {
    struct session_data *data = (struct session_data *) user;
    switch ( reason ) {
        case LWS_CALLBACK_ESTABLISHED:       // 当服务器和客户端完成握手后
            printf("Client connect!\n");
            break;
        case LWS_CALLBACK_RECEIVE:           // 当接收到客户端发来的帧以后
            // 判断是否最后一帧
            data->fin = lws_is_final_fragment( wsi );
            // 判断是否二进制消息
            data->bin = lws_frame_is_binary( wsi );
            // 对服务器的接收端进行流量控制，如果来不及处理，可以控制之
            // 下面的调用禁止在此连接上接收数据
            lws_rx_flow_control( wsi, 0 );
 
            // 业务处理部分，为了实现Echo服务器，把客户端数据保存起来
            memcpy( &data->buf[ LWS_PRE ], in, len );
            data->len = len;
            printf("recvied message:%s\n",in);
 
            // 需要给客户端应答时，触发一次写回调
            lws_callback_on_writable( wsi );
            break;
        case LWS_CALLBACK_SERVER_WRITEABLE:   // 当此连接可写时
            lws_write( wsi, &data->buf[ LWS_PRE ], data->len, LWS_WRITE_TEXT );
            // 下面的调用允许在此连接上接收数据
            lws_rx_flow_control( wsi, 1 );
            break;
    }
    // 回调函数最终要返回0，否则无法创建服务器
    return 0;
}
 
/**
 * 支持的WebSocket子协议数组
 * 子协议即JavaScript客户端WebSocket(url, protocols)第2参数数组的元素
 * 你需要为每种协议提供回调函数
 */
struct lws_protocols protocols[] = {
    {
        //协议名称，协议回调，接收缓冲区大小
        "ws", protocol_my_callback, sizeof( struct session_data ), MAX_PAYLOAD_SIZE,
    },
    {
        NULL, NULL,   0 // 最后一个元素固定为此格式
    }
};
 
int main(int argc,char **argv)
{
    // 信号处理函数
    signal( SIGTERM, sighdl );
 
    struct lws_context_creation_info ctx_info = { 0 };
    ctx_info.port = 7681;
    ctx_info.iface = NULL; // 在所有网络接口上监听
    ctx_info.protocols = protocols;
    ctx_info.gid = -1;
    ctx_info.uid = -1;
    ctx_info.options = LWS_SERVER_OPTION_VALIDATE_UTF8;
 
    // ctx_info.ssl_ca_filepath = "../ca/ca-cert.pem";
    // ctx_info.ssl_cert_filepath = "./server-cert.pem";
    // ctx_info.ssl_private_key_filepath = "./server-key.pem";
    ctx_info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
    //ctx_info.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
    
    struct lws_context *context = lws_create_context(&ctx_info);
    while ( !exit_sig ) {
        lws_service(context, 1000);
    }
    lws_context_destroy(context);
 
    return 0;
}